/*
 * Copyright (c) 2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "sec_storage_ipc_proxy_inner.h"

#include <securec.h>
#include <stddef.h>

#include "logger.h"
#include "parcel.h"

#include "sec_storage_ipc_defines.h"

bool ParcelWriteStorageIndicator(Parcel *parcel, const StorageIndicator *indicator);

ResultCode SetFactoryResetAuthenticationKeyInputToBuffer(FactoryResetLevel level, FactoryResetAuthAlgo algo,
    const StorageAuthKey *key, uint8_t *buffer, uint32_t *size)
{
    if (key == NULL || buffer == NULL || size == NULL) {
        return INVALID_PARA_NULL_PTR;
    }

    ResultCode ret = MEM_WRITE_ERR;
    Parcel parcel = CreateParcel(*size);
    do {
        if (!ParcelWriteUint32(&parcel, level)) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_1;
            LOG_ERROR("ParcelWriteUint32 level error");
            break;
        }

        if (!ParcelWriteUint32(&parcel, algo)) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_2;
            LOG_ERROR("ParcelWriteUint32 algo error");
            break;
        }

        if (!ParcelWriteUint32(&parcel, key->keySize)) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_3;
            LOG_ERROR("ParcelWriteUint32 keySize error");
            break;
        }

        if (!ParcelWrite(&parcel, key->keyData, key->keySize)) {
            ret = IPC_PARA_PROCESS_ERR_INDEX_4;
            LOG_ERROR("ParcelWriteUint32 keyData error, size is %u", key->keySize);
            break;
        }

        if (!ParcelToDataBuffer(&parcel, buffer, size)) {
            ret = MEM_COPY_ERR;
            LOG_ERROR("ParcelToDataBuffer keyData error");
            break;
        }
        ret = SUCCESS;
    } while (0);

    DeleteParcel(&parcel);
    return ret;
}

ResultCode PrepareFactoryResetOutputFromBuffer(const uint8_t *buffer, uint32_t size, uint8_t *nonce, uint32_t *length)
{
    if (buffer == NULL || size == 0) {
        return INVALID_PARA_NULL_PTR;
    }

    if (nonce == NULL || length == NULL) {
        return INVALID_PARA_NULL_PTR;
    }

    ResultCode ret = IPC_RES_DATA_ERR;
    Parcel parcel = CreateParcelWithData(buffer, size);
    do {
        uint32_t count = 0;
        if (!ParcelReadUint32(&parcel, &count)) {
            LOG_ERROR("read nonce size error");
            break;
        }

        if (count > *length) {
            LOG_ERROR("input buffer is too small(%u > %u)", count, *length);
            break;
        }

        if (!ParcelRead(&parcel, nonce, count)) {
            LOG_ERROR("read nonce data error");
            break;
        }
        *length = count;

        ret = SUCCESS;
    } while (0);

    DeleteParcel(&parcel);
    return ret;
}

ResultCode ProcessFactoryResetInputToBuffer(FactoryResetLevel level, const uint8_t *credential, uint32_t length,
    uint8_t *buffer, uint32_t *size)
{
    if (credential == NULL || buffer == NULL || size == NULL) {
        return INVALID_PARA_NULL_PTR;
    }

    ResultCode ret = MEM_WRITE_ERR;
    Parcel parcel = CreateParcel(*size);
    do {
        if (!ParcelWriteUint32(&parcel, level)) {
            LOG_ERROR("ParcelWriteUint32 level error");
            break;
        }

        if (!ParcelWriteUint32(&parcel, length)) {
            LOG_ERROR("ParcelWriteUint32 algo error");
            break;
        }

        if (!ParcelWrite(&parcel, credential, length)) {
            LOG_ERROR("ParcelWriteUint32 keyData error, size is %u", length);
            break;
        }

        if (!ParcelToDataBuffer(&parcel, buffer, size)) {
            ret = MEM_COPY_ERR;
            LOG_ERROR("ParcelToDataBuffer keyData error");
            break;
        }
        ret = SUCCESS;
    } while (0);

    DeleteParcel(&parcel);
    return ret;
}

ResultCode AllocateSlotInputToBuffer(const StorageFileName *name, const StorageSlotAttr *slotAttr,
    const StorageAuthKey *slotKey, uint8_t *buffer, uint32_t *size)
{
    if (name == NULL || slotAttr == NULL || slotKey == NULL) {
        return INVALID_PARA_NULL_PTR;
    }

    if (buffer == NULL || size == NULL) {
        return INVALID_PARA_NULL_PTR;
    }

    ResultCode ret = MEM_WRITE_ERR;
    Parcel parcel = CreateParcel(*size);
    do {
        if (!ParcelWrite(&parcel, (const uint8_t *)name, sizeof(StorageFileName))) {
            LOG_ERROR("ParcelWrite fileHandle error");
            break;
        }

        if (!ParcelWrite(&parcel, (const uint8_t *)slotAttr, sizeof(StorageSlotAttr))) {
            LOG_ERROR("ParcelWrite slotAttr error, size is %u", (uint32_t)sizeof(StorageSlotAttr));
            break;
        }

        if (!ParcelWriteUint32(&parcel, slotKey->keySize)) {
            LOG_ERROR("ParcelWriteUint32 keySize error");
            break;
        }

        if (!ParcelWrite(&parcel, slotKey->keyData, slotKey->keySize)) {
            LOG_ERROR("ParcelWrite keyData error, size is %u", slotKey->keySize);
            break;
        }

        if (!ParcelToDataBuffer(&parcel, buffer, size)) {
            ret = MEM_COPY_ERR;
            LOG_ERROR("ParcelToDataBuffer keyData error");
            break;
        }
        ret = SUCCESS;
    } while (0);

    DeleteParcel(&parcel);
    return ret;
}

ResultCode GetSlotStatusInputToBuffer(const StorageFileName *name, uint8_t *buffer, uint32_t *size)
{
    if (name == NULL) {
        return INVALID_PARA_NULL_PTR;
    }

    if (buffer == NULL || size == NULL) {
        return INVALID_PARA_NULL_PTR;
    }

    ResultCode ret = MEM_WRITE_ERR;
    Parcel parcel = CreateParcel(*size);
    do {
        if (!ParcelWrite(&parcel, (const uint8_t *)name, sizeof(StorageFileName))) {
            LOG_ERROR("ParcelWrite fileName error");
            break;
        }

        if (!ParcelToDataBuffer(&parcel, buffer, size)) {
            ret = MEM_COPY_ERR;
            LOG_ERROR("ParcelToDataBuffer keyData error");
            break;
        }
        ret = SUCCESS;
    } while (0);

    DeleteParcel(&parcel);
    return ret;
}

ResultCode WriteSlotInputToBuffer(const StorageIndicator *indicator, const StorageDataBuffer *data, uint8_t *buffer,
    uint32_t *size)
{
    if (indicator == NULL || data == NULL) {
        return INVALID_PARA_NULL_PTR;
    }

    if (indicator->name == NULL || indicator->key == NULL || indicator->area == NULL) {
        return INVALID_PARA_NULL_PTR;
    }

    if (buffer == NULL || size == NULL) {
        return INVALID_PARA_NULL_PTR;
    }

    ResultCode ret = MEM_WRITE_ERR;
    Parcel parcel = CreateParcel(*size);
    do {
        if (!ParcelWriteStorageIndicator(&parcel, indicator)) {
            LOG_ERROR("ParcelWriteStorageIndicator error");
            break;
        }

        if (!ParcelWriteUint32(&parcel, data->bufferSize)) {
            LOG_ERROR("ParcelWriteUint32 bufferSize error");
            break;
        }

        if (!ParcelWrite(&parcel, data->bufferData, data->bufferSize)) {
            LOG_ERROR("ParcelWriteUint32 keyData error, size is %u", data->bufferSize);
            break;
        }

        if (!ParcelToDataBuffer(&parcel, buffer, size)) {
            ret = MEM_COPY_ERR;
            LOG_ERROR("ParcelToDataBuffer keyData error");
            break;
        }
        ret = SUCCESS;
    } while (0);

    DeleteParcel(&parcel);
    return ret;
}

ResultCode ReadSlotInputToBuffer(const StorageIndicator *indicator, uint8_t *buffer, uint32_t *size)
{
    if (indicator == NULL) {
        return INVALID_PARA_NULL_PTR;
    }

    if (buffer == NULL || size == NULL) {
        return INVALID_PARA_NULL_PTR;
    }

    ResultCode ret = MEM_WRITE_ERR;
    Parcel parcel = CreateParcel(*size);
    do {
        if (!ParcelWriteStorageIndicator(&parcel, indicator)) {
            LOG_ERROR("ParcelWriteStorageIndicator error");
            break;
        }

        if (!ParcelToDataBuffer(&parcel, buffer, size)) {
            ret = MEM_COPY_ERR;
            LOG_ERROR("ParcelToDataBuffer keyData error");
            break;
        }
        ret = SUCCESS;
    } while (0);

    DeleteParcel(&parcel);
    return ret;
}

ResultCode FreeSlotInputToBuffer(const StorageFileName *name, const StorageAuthKey *key, uint8_t *buffer,
    uint32_t *size)
{
    if (name == NULL || key == NULL) {
        return INVALID_PARA_NULL_PTR;
    }

    if (buffer == NULL || size == NULL) {
        return INVALID_PARA_NULL_PTR;
    }

    ResultCode ret = MEM_WRITE_ERR;
    Parcel parcel = CreateParcel(*size);
    do {
        if (!ParcelWrite(&parcel, (const uint8_t *)name, sizeof(StorageFileName))) {
            LOG_ERROR("ParcelWriteStorageName error");
            break;
        }

        if (!ParcelWriteUint32(&parcel, key->keySize)) {
            LOG_ERROR("ParcelWriteUint32 keySize error");
            break;
        }

        if (!ParcelWrite(&parcel, key->keyData, key->keySize)) {
            LOG_ERROR("ParcelWriteUint32 keyData error, size is %u", key->keySize);
            break;
        }

        if (!ParcelToDataBuffer(&parcel, buffer, size)) {
            ret = MEM_COPY_ERR;
            LOG_ERROR("ParcelToDataBuffer keyData error");
            break;
        }
        ret = SUCCESS;
    } while (0);

    DeleteParcel(&parcel);
    return ret;
}

ResultCode SetAllSlotsSizeInputToBuffer(const uint16_t *slotSizeArray, uint32_t arrayLength, uint8_t *buffer,
    uint32_t *size)
{
    if (slotSizeArray == NULL || arrayLength == 0) {
        return INVALID_PARA_NULL_PTR;
    }

    if (arrayLength > MAX_SLOTS_NUM) {
        return INVALID_PARA_ERR_SIZE;
    }

    if (buffer == NULL || size == NULL) {
        return INVALID_PARA_NULL_PTR;
    }

    ResultCode ret = MEM_WRITE_ERR;
    Parcel parcel = CreateParcel(*size);
    do {
        if (!ParcelWriteUint32(&parcel, arrayLength)) {
            LOG_ERROR("ParcelWriteUint32 arrayLength error");
            break;
        }

        if (!ParcelWrite(&parcel, (const uint8_t *)slotSizeArray, arrayLength * sizeof(uint16_t))) {
            LOG_ERROR("ParcelWrite slotSizeArray failed");
            break;
        }

        if (!ParcelToDataBuffer(&parcel, buffer, size)) {
            ret = MEM_COPY_ERR;
            LOG_ERROR("ParcelToDataBuffer keyData error");
            break;
        }
        ret = SUCCESS;
    } while (0);

    DeleteParcel(&parcel);
    return ret;
}

ResultCode GetAllSlotsSizeOutputFromBuffer(const uint8_t *buffer, uint32_t size, uint16_t *slotSizeArray,
    uint32_t *arrayLength)
{
    if (buffer == NULL || size == 0) {
        return INVALID_PARA_NULL_PTR;
    }

    if (slotSizeArray == NULL || arrayLength == NULL) {
        return INVALID_PARA_NULL_PTR;
    }

    ResultCode ret = IPC_RES_DATA_ERR;
    Parcel parcel = CreateParcelWithData(buffer, size);
    do {
        uint32_t count = 0;
        if (!ParcelReadUint32(&parcel, &count)) {
            LOG_ERROR("read size data error");
            break;
        }

        if (count > MAX_SLOTS_NUM) {
            LOG_ERROR("read size data is too big = %u", count);
            break;
        }

        uint32_t readSize = count * sizeof(uint16_t);
        if (readSize > *arrayLength) {
            LOG_ERROR("input buffer is too small(%u > %u)", readSize, *arrayLength);
            break;
        }

        if (!ParcelRead(&parcel, (uint8_t *)slotSizeArray, readSize)) {
            LOG_ERROR("read slotSizeArray data error, size = %u", readSize);
            break;
        }

        *arrayLength = count;

        ret = SUCCESS;
    } while (0);

    DeleteParcel(&parcel);
    return ret;
}

// private impl
bool ParcelWriteStorageIndicator(Parcel *parcel, const StorageIndicator *indicator)
{
    if (parcel == NULL || indicator == NULL) {
        return false;
    }

    const StorageFileName *name = indicator->name;
    if (!ParcelWrite(parcel, (const uint8_t *)name, sizeof(StorageFileName))) {
        LOG_ERROR("ParcelWriteStorageName error");
        return false;
    }

    const StorageAuthKey *slotKey = indicator->key;
    if (!ParcelWriteUint32(parcel, slotKey->keySize)) {
        LOG_ERROR("ParcelWriteUint32 keySize error");
        return false;
    }

    if (!ParcelWrite(parcel, slotKey->keyData, slotKey->keySize)) {
        LOG_ERROR("ParcelWriteUint32 keyData error, size is %u", slotKey->keySize);
        return false;
    }

    const StorageDataArea *area = indicator->area;
    if (!ParcelWrite(parcel, (const uint8_t *)area, sizeof(StorageDataArea))) {
        LOG_ERROR("ParcelWriteUint32 StorageDataArea error");
        return false;
    }
    return true;
}